﻿using System;
using System.Collections.Generic;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries.Utilities;
using NetTopologySuite.Triangulate.QuadEdge;

namespace NetTopologySuite.Triangulate
{
	public class ConformingDelaunayTriangulationBuilder
	{
		public void SetSites(IGeometry sites)
		{
			this._siteCoords = DelaunayTriangulationBuilder.ExtractUniqueCoordinates(sites);
		}

		public IGeometry Constraints
		{
			set
			{
				this._constraintLines = value;
			}
		}

		public double Tolerance
		{
			get
			{
				return this._tolerance;
			}
			set
			{
				this._tolerance = value;
			}
		}

		private void Create()
		{
			if (this._subdiv != null)
			{
				return;
			}
			Envelope envelope = DelaunayTriangulationBuilder.Envelope(this._siteCoords);
			IList<Segment> segments = new List<Segment>();
			if (this._constraintLines != null)
			{
				envelope.ExpandToInclude(this._constraintLines.EnvelopeInternal);
				this.CreateVertices(this._constraintLines);
				segments = ConformingDelaunayTriangulationBuilder.CreateConstraintSegments(this._constraintLines);
			}
			ConformingDelaunayTriangulator conformingDelaunayTriangulator = new ConformingDelaunayTriangulator(this.CreateSiteVertices(this._siteCoords), this._tolerance);
			conformingDelaunayTriangulator.SetConstraints(segments, new List<Vertex>(this._constraintVertexMap.Values));
			conformingDelaunayTriangulator.FormInitialDelaunay();
			conformingDelaunayTriangulator.EnforceConstraints();
			this._subdiv = conformingDelaunayTriangulator.Subdivision;
		}

		private IEnumerable<Vertex> CreateSiteVertices(IEnumerable<Coordinate> coords)
		{
			List<Vertex> list = new List<Vertex>();
			foreach (Coordinate coordinate in coords)
			{
				if (!this._constraintVertexMap.ContainsKey(coordinate))
				{
					list.Add(new ConstraintVertex(coordinate));
				}
			}
			return list;
		}

		private void CreateVertices(IGeometry geom)
		{
			Coordinate[] coordinates = geom.Coordinates;
			for (int i = 0; i < coordinates.Length; i++)
			{
				Vertex value = new ConstraintVertex(coordinates[i]);
				this._constraintVertexMap.Add(coordinates[i], value);
			}
		}

		private static IList<Segment> CreateConstraintSegments(IGeometry geom)
		{
			IEnumerable<IGeometry> lines = LinearComponentExtracter.GetLines(geom);
			List<Segment> list = new List<Segment>();
			foreach (IGeometry geometry in lines)
			{
				ConformingDelaunayTriangulationBuilder.CreateConstraintSegments((ILineString)geometry, list);
			}
			return list;
		}

		private static void CreateConstraintSegments(ILineString line, IList<Segment> constraintSegs)
		{
			Coordinate[] coordinates = line.Coordinates;
			for (int i = 1; i < coordinates.Length; i++)
			{
				constraintSegs.Add(new Segment(coordinates[i - 1], coordinates[i]));
			}
		}

		public QuadEdgeSubdivision GetSubdivision()
		{
			this.Create();
			return this._subdiv;
		}

		public IMultiLineString GetEdges(IGeometryFactory geomFact)
		{
			this.Create();
			return this._subdiv.GetEdges(geomFact);
		}

		public IGeometryCollection GetTriangles(IGeometryFactory geomFact)
		{
			this.Create();
			return this._subdiv.GetTriangles(geomFact);
		}

		private ICollection<Coordinate> _siteCoords;

		private IGeometry _constraintLines;

		private double _tolerance;

		private QuadEdgeSubdivision _subdiv;

		private readonly IDictionary<Coordinate, Vertex> _constraintVertexMap = new SortedDictionary<Coordinate, Vertex>();
	}
}
